﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Castle.DynamicProxy;
using EAV.Data;
using System.Reflection;
using System.Collections;
using System.Data.Objects;

namespace EAV.Bridge
{
    public class ItemFactory
    {
        static ProxyGenerator _generator;
        static EAVContext _context;

        static ItemFactory()
        {
            _generator = new ProxyGenerator();
            _context = new EAVContext();
        }
        public static T New<T>() where T : BaseItem
        {
            TypeInfo info = TypeInfo.GetInfo(typeof(T));

            //entity
            Entity e = new Entity();

            e.ID = Guid.NewGuid();
            e.CreationDate = DateTime.Now;
            e.Name = info.MapTypeOn;

            _context.AddToEntity(e);

            //interceptor
            ItemInterceptor interceptor = new ItemInterceptor(e, info, _context);

            //proxy
            T proxy = _generator.CreateClassProxy<T>(interceptor);

            //relations
            foreach (string relation in info.Relations.Keys)
            {
                CreateRelationProxy(typeof(T), info, e, proxy, relation);
            }
            return proxy;
        }

        private static object CreateRelationProxy(Type type, TypeInfo info, Entity e, object proxy, string relation)
        {
            PropertyInfo relationProperty = type.GetProperty(relation);

            RelationInterceptor relationInterceptor = new RelationInterceptor(info.Relations[relation].ForeignKey, e.ID);

            object relationProxy = _generator.CreateClassProxy(relationProperty.PropertyType, relationInterceptor);

            relationProperty.SetValue(
                proxy,
                relationProxy,
                null);

            return relationProxy;
        }
        private static IList FromDB(Type type, ReadMode readMode, FilterMode filterMode, params Filter[] filters)
        {
            TypeInfo info = TypeInfo.GetInfo(type);

            IEnumerable<Entity> entities = filters.Length == 0 ?
                _context.Entity.Where(e => e.Name == info.MapTypeOn && e.IsDeleted == false).AsEnumerable<Entity>() :
                LoadFromDB(filterMode, filters);

            System.Collections.ArrayList items = new System.Collections.ArrayList();

            foreach (Entity e in entities)
            {
                object proxy = _generator.CreateClassProxy(type, new ItemInterceptor(e, info, _context));

                items.Add(proxy);


                foreach (string relation in info.Relations.Keys)
                {
                    object relationProxy = CreateRelationProxy(type, info, e, proxy, relation);

                    if (readMode == ReadMode.Eager)
                    {
                        string foreignKey = info.Relations[relation].ForeignKey;
                        Type relatedType = info.Relations[relation].RelatedType;
                        TypeInfo relatedInfo = TypeInfo.GetInfo(relatedType);

                        Filter filter = new Filter(foreignKey, FilterOperator.Equal, e.ID);
                        //IEnumerable<Entity> relatedEntities = LoadFromDB(FilterMode.And, filter);

                        object relatedItems = FromDB(relatedType, readMode, FilterMode.And, filter);



                        foreach (BaseItem relatedItem in relatedItems as IEnumerable)
                        {
                            MethodInfo add = relationProxy.GetType().GetMethod("Add");

                            add.Invoke(relationProxy, new object[] { relatedItem });
                        }
                    }
                }
            }

            return items;
        }
        public static IList<T> FromDB<T>(ReadMode readMode, FilterMode filterMode, params Filter[] filters) where T : BaseItem
        {
            IList items = FromDB(typeof(T), readMode, filterMode, filters);

            List<T> typedItems = new List<T>();

            foreach (T item in items)
            {
                typedItems.Add(item);
            }

            return typedItems;
        }
        private static IEnumerable<Entity> LoadFromDB(FilterMode mode, params Filter[] filters)
        {
            string sql = SQLBuilder.GetSql(mode, filters);

            ObjectQuery<Entity> query = new ObjectQuery<Entity>(sql, _context);

            return query.AsEnumerable<Entity>();
        }
    }
}
